/*
 * Created on 2005-3-18
 *
 */
package com.hd123.rumba.request;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Iterator;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.hd123.rumba.domain.IDomain;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.exception.ParameterException;
import com.hd123.rumba.log.ActionLogger;
import com.hd123.rumba.log.RumbaLogger;
import com.hd123.rumba.organization.IOrganization;
import com.hd123.rumba.runtime.IVersionedPersistent;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.biz.BVersionedPersistent;
import com.hd123.rumba.sql.biz.BResultPage;
import com.hd123.rumba.sql.biz.BSQLSubquery;
import com.hd123.rumba.sql.biz.IBQueryMgr;
import com.hd123.rumba.user.IUser;
import com.hd123.rumba.user.PE;
import com.hd123.rumba.util.StringUtil;

/**
 * Action的抽象实现。作为所有Action的基类。
 * <p>
 * 
 * <b>注：</b>要求具体的Action必须定义标题caption（提供给操作日志使用），具体定义一个名为"CAPTION_VIEW"的常量：<br>
 * <code>public static final String CAPTION_VIEW = "<i>Action标题</i>";</code>
 * 
 * @author caili
 */
public abstract class ActionAbstractImpl implements Action {

  protected Dictionary dd;
  private String caption = this.getClass().getSimpleName();
  /** 提供默认的操作日志生成器。 */
  protected ActionLogger logger = new ActionLogger(this);

  public void init(Dictionary dd) {
    this.dd = dd;

    // 取得Action的标题Caption
    Field[] fields = this.getClass().getDeclaredFields();
    for (int i = 0; i < fields.length; i++)
      try {
        if ("CAPTION_VIEW".equals(fields[i].getName()))
          caption = (String) fields[i].get(null);
      } catch (IllegalArgumentException e) {
      } catch (IllegalAccessException e) {
      }
  }

  public Dictionary getDd() {
    return dd;
  }

  public String getCaption() {
    return this.caption;
  }

  public abstract String execute() throws Exception;

  public String paging() throws Exception {
    // get page first
    Integer page = null;
    String prefix = (String) dd.getFormValue("PageNumberPrefix");
    if (prefix == null)
      prefix = "PageNumber_";
    Iterator i = dd.createFormPrefixedKeysIterator(prefix);
    if (i.hasNext()) {
      String key = (String) i.next();
      page = Integer.valueOf(key);
    } else {
      String currentPage = (String) dd.getFormValue("CurrentPageNumber");
      if (currentPage != null)
        page = Integer.valueOf(currentPage);
    }
    String url = ActionConstants.NOTIMPLEMENTED;
    // ensure the request is submitted by a page turn
    if (page != null) {
      url = paging(page.intValue());
    }
    return url;
  }

  /**
   * to be override by subclass
   * 
   * @param page
   * @return
   * @throws Exception
   */
  protected String paging(int page) throws Exception {
    return ActionConstants.NOTIMPLEMENTED;
  }

  protected final Hashtable getContentTypes() {
    Hashtable contentTypes = new Hashtable(12);
    contentTypes.put("doc", "application/msword");
    contentTypes.put("pdf", "application/pdf");
    contentTypes.put("xls", "application/vnd.ms-excel");
    contentTypes.put("ppt", "application/vnd.ms-powerpoint");
    contentTypes.put("exe", "application/x-msdos-program");
    contentTypes.put("swf", "application/x-schockwave-flasch");
    contentTypes.put("zip", "application/zip");
    contentTypes.put("htm", "text/html");
    contentTypes.put("html", "text/html");
    contentTypes.put("xml", "text/xml");
    contentTypes.put("mpeg", "video/mpeg");
    contentTypes.put("jpg", "image/jpeg");
    contentTypes.put("gif", "image/gif");
    return contentTypes;
  }

  private final String getContentType(String s) {
    if (s == null)
      return null;
    else
      return (String) getContentTypes().get(s.substring(s.lastIndexOf(".") + 1));
  }

  /**
   * Response with the file as an attachment
   * 
   * @param servletresponse
   * @param file
   * @param sContentType
   * @throws IOException
   */
  public void writeFileContent(HttpServletResponse servletresponse, File file, String sContentType)
      throws IOException {
    BufferedInputStream bis = null;
    // Object obj = null;
    try {
      bis = new BufferedInputStream(new FileInputStream(file));
      if (sContentType == null || sContentType.length() == 0)
        sContentType = getContentType(file.getName());
      writeStreamContent(servletresponse, bis, file.getName(), sContentType);
    } finally {
      if (bis != null)
        try {
          bis.close();
        } catch (Exception exception1) {
          Logger.getLogger(this.getClass()).warn(exception1.getMessage());
        }
    }
  }

  /**
   * Response with the stream content as an attachment
   * 
   * @param servletresponse
   * @param ins
   * @param filename
   * @param sContentType
   * @throws IOException
   */
  public void writeStreamContent(HttpServletResponse servletresponse, InputStream ins,
      String filename, String sContentType) throws IOException {

    if (sContentType != null)
      servletresponse.setContentType(sContentType);
    servletresponse.setCharacterEncoding(StringUtil.ENCODING_CHARSET);
    servletresponse.setHeader("Content-Type", "charset=" + StringUtil.ENCODING_CHARSET);
    servletresponse.setHeader("Content-Disposition", "attachment;filename="
        + StringUtil.encodeFileName(filename));
    ServletOutputStream out = servletresponse.getOutputStream();
    byte abyte0[] = new byte[2048];
    int bytesRead, len = 0;
    while (-1 != (bytesRead = ins.read(abyte0, 0, abyte0.length))) {
      len += bytesRead;
      out.write(abyte0, 0, bytesRead);
    }
    servletresponse.setContentLength(len);
    out.flush();
  }

  /**
   * Response with the file content
   * 
   * @param servletresponse
   * @param file
   * @param sContentType
   * @throws IOException
   */
  public void writeFile(HttpServletResponse servletresponse, File file, String sContentType)
      throws IOException {
    BufferedInputStream bis = null;
    // Object obj = null;
    try {
      bis = new BufferedInputStream(new FileInputStream(file));
      if (sContentType == null || sContentType.length() == 0)
        sContentType = getContentType(file.getName());
      writeStream(servletresponse, bis, sContentType);
    } finally {
      if (bis != null)
        try {
          bis.close();
        } catch (Exception exception1) {
          Logger.getLogger(this.getClass()).warn(exception1.getMessage());
        }
    }
  }

  /**
   * Response with the stream content
   * 
   * @param servletresponse
   * @param ins
   * @param filename
   * @param sContentType
   * @throws IOException
   */
  public void writeStream(HttpServletResponse servletresponse, InputStream ins, String sContentType)
      throws IOException {

    if (sContentType != null)
      servletresponse.setContentType(sContentType);
    servletresponse.setCharacterEncoding(StringUtil.ENCODING_CHARSET);
    ServletOutputStream out = servletresponse.getOutputStream();
    byte abyte0[] = new byte[2048];
    int bytesRead, len = 0;
    while (-1 != (bytesRead = ins.read(abyte0, 0, abyte0.length))) {
      len += bytesRead;
      out.write(abyte0, 0, bytesRead);
    }
    servletresponse.setContentLength(len);
    out.flush();
  }

  protected void checkParameterNotNull(Object param, String name) throws ParameterException {
    if (param == null)
      throw new ParameterException("缺少必要的参数" + name);
  }

  protected String executeWithPermission(String functionName, String permissionId) throws Exception {
    if (dd.getCurrentUser() == null)
      return showSessionTimeout();
    if (permissionId == null || dd.getCurrentUser().hasPermission(permissionId)) {
      Method m = this.getClass().getMethod(functionName, (Class[]) null);
      return (String) m.invoke((Object) this, (Object[]) null);
    }
    return showNoAccess();
  }

  protected String executeWithPermissions(String functionName, PE pe) throws Exception {
    if (dd.getCurrentUser() == null)
      return showSessionTimeout();
    if (pe == null || dd.getCurrentUser().hasPermissions(pe)) {
      Method m = this.getClass().getMethod(functionName, (Class[]) null);
      return (String) m.invoke((Object) this, (Object[]) null);
    }
    return showNoAccess();
  }

  /**
   * 只需要permissionIds其中的一个权限便可执行functionName
   * 
   * @param functionName
   * @param permissionIds
   * @return
   * @throws Exception
   */
  protected String executeWithPermissions(String functionName, String[] permissionIds)
      throws Exception {
    if (dd.getCurrentUser() == null)
      return showSessionTimeout();
    boolean ok = permissionIds == null;
    if (!ok) {
      for (int i = 0; !ok && i < permissionIds.length; ++i)
        ok = dd.getCurrentUser().hasPermission(permissionIds[i]);
    }
    if (ok) {
      Method m = this.getClass().getMethod(functionName, (Class[]) null);
      return (String) m.invoke((Object) this, (Object[]) null);
    }
    return showNoAccess();
  }

  /**
   * 需要permissionIds中的所有权限才可执行functionName
   * 
   * @param functionName
   * @param permissionIds
   * @return
   * @throws Exception
   */
  protected String executeWithAllPermissions(String functionName, String[] permissionIds)
      throws Exception {
    if (dd.getCurrentUser() == null)
      return showSessionTimeout();
    boolean ok = permissionIds == null;
    if (!ok) {
      ok = true;
      for (int i = 0; i < permissionIds.length; ++i)
        ok = ok && dd.getCurrentUser().hasPermission(permissionIds[i]);
    }
    if (ok) {
      Method m = this.getClass().getMethod(functionName, (Class[]) null);
      return (String) m.invoke((Object) this, (Object[]) null);
    }
    return showNoAccess();
  }

  public String writeStaticContent(String name) throws Exception {
    File contextDir = new File(dd.getRequest().getSession().getServletContext().getRealPath("."));
    File file = new File(contextDir.getParentFile().getParentFile(), JspLib.getStaticContent(name));
    if (file.exists()) {
      FileInputStream ins = new FileInputStream(file);
      try {
        writeStreamContent(dd.getResponse(), ins, name, null);
      } finally {
        ins.close();
      }
    }
    return ActionConstants.DIRECT;
  }

  /**
   * 取得当前登录用户。取自dd或BuffaloSession
   * 
   * @return
   */
  public IUser getCurrentUser() {
    return (dd != null) ? dd.getCurrentUser() : BuffaloSession.getCurrentUser();
  }

  /**
   * 取得当前登录域。取自dd或BuffaloSession
   * 
   * @return
   */
  public IDomain getCurrentDomain() {
    return (dd != null) ? dd.getCurrentDomain() : BuffaloSession.getCurrentDomain();
  }

  /**
   * 取得当前登录组织。取自dd或BuffaloSession
   * 
   * @return
   */
  public IOrganization getCurrentOrganization() {
    return (dd != null) ? dd.getCurrentOrganization() : BuffaloSession.getCurrentOrganization();
  }

  /**
   * 执行查询。客户端通过buffalo。
   * 
   * @param bsq
   *          查询定义
   * @param pageSize
   * @param page
   * @return 返回查询结果集。
   * @throws Exception
   */
  public BResultPage query(BSQLSubquery bsq, int pageSize, int page) throws Exception {
    try {
      IBQueryMgr qryMgr = (IBQueryMgr) NamingMgr.getInstance().lookupMgr(IBQueryMgr.class);
      return qryMgr.query(bsq, pageSize, page);
    } catch (Exception e) {
      this.reportError("", e);
      throw e;
    }
  }

  /**
   * 提供对于实体的共享检查。错误信息以BusinessException方式返回。
   * 
   * @param entity
   *          被检查的实体对象。
   * @param oca
   * @param classCaption
   *          实体名，例如：IUser.CAPTION_CLASS
   * @param entitySpec
   *          实体描述，例如：user.getCodeName();
   * @throws BusinessException
   * @deprecated
   */
  protected void checkOca(IVersionedPersistent entity, long oca, String classCaption,
      String entitySpec) throws BusinessException {
    if (entity.getOca() != oca)
      throw new BusinessException("指定" + classCaption + "已经被其他用户修改" + "，您所做的修改将被取消(" + entitySpec
          + ")。");
  }

  /**
   * 提供对于实体的共享检查。若版本不符合则将在数据字典中加入一条警告。
   * 
   * @param entity
   *          被检查的实体对象。
   * @param oca
   * @param classCaption
   *          实体名，例如：IUser.CAPTION_CLASS
   * @throws BusinessException
   */
  protected void checkOca(BVersionedPersistent entity, long oca, String classCaption)
      throws BusinessException {
    if (entity.getOca() != oca)
      dd.addWarning("当前" + classCaption + "已经被其他人修改。");
  }

  /**
   * 统一的异常报告处理。
   * 
   * @param message
   * @param e
   */
  protected void reportError(Object message, Throwable t) {
    RumbaLogger.getLogger(this.getClass()).error(message, t);
  }

  /**
   * 显示无权访问页面。
   * 
   * @return
   * @throws Exception
   */
  protected String showNoAccess() throws Exception {
    dd.setWorkingTemplate(ActionConstants.NOACCESS);
    return ActionConstants.APPLICATIONFRAME;
  }

  /**
   * 显示会话超时页面。
   * 
   * @return
   * @throws Exception
   */
  private String showSessionTimeout() throws Exception {
    dd.setWorkingTemplate(ActionConstants.SESSIONTIMEOUT);
    return ActionConstants.APPLICATIONFRAME;
  }

}
